5.08. ООП
ООП
Почему ST «чистое ООП»? Потому что нет функций, операторов, процедур. Только объекты. Поэтому и ОБЪЕКТНО-ориентированное программирование.
Помните основы ООП?
Наследование, полиморфизм, абстракция и инкапсуляция, так?
Класс — это шаблон для создания объектов.
В Smalltalk класс определяется как объект, отвечающий за создание и поведение своих экземпляров. То есть, в этом языке класс не просто код, а сам является объектом.
Создание класса:
Object subclass: #Person
instanceVariableNames: 'name age'
classVariableNames: ''
package: 'MyApp'
Здесь:
Object— родительский класс.#Person— имя нового класса.'name age'— экземплярные переменные (приватные).package: 'MyApp'— пространство имён.
Создание объекта (экземпляра):
| person |
person := Person new.
new — это сообщение, отправленное классу Person.
Причем, класс Person — это объект, экземпляр своего метакласса. Вы можете:
Person class "→ Metaclass of Person"
Person superclass "→ Object"
Person methods "→ список всех методов"
Person numberOfInstances "→ сколько объектов создано"
Именно так и можно в идеале реализовывать паттерны вроде Singleton, фабрики, рефлексию — прямо в языке. И всё что есть у класса, можно посмотреть прямо в инспекторе, как мы видели в Pharo.
Метод — это реализация ответа на сообщение.
Пример метода:
getName
^ name
setName: aName
name := aName
introduceYourself
^ 'Hi, I am ', name, ' and I am ', age printString, ' years old.'
^ — возврат значения (аналог return). Мы именно так и создали наш метод, который выводил (возвращал) Hello World!
setName: aName — ключевое сообщение с одним параметром.
Методы не принадлежат объекту напрямую — они хранятся в классе. Объект знает, где искать метод — по цепочке наследования.
Говоря о наследовании. Smalltalk поддерживает одиночное наследование. Нет множественного наследования — но и не нужно.
Object subclass: #Animal
instanceVariableNames: 'name'
...
Animal subclass: #Dog
instanceVariableNames: 'breed'
...
Теперь Dog наследует все переменные и методы от Animal.
Помните про переопределение? В ST тоже есть переопределение метода:
Dog >> makeSound
^ 'Woof!'
Вызов родительского метода:
Dog >> introduce
^ super introduce, ' I am a dog.'
super — специальное ключевое слово, указывающее на родительский класс.
Вместо этого — композиция, делегирование, поведение через блоки.
А как с инкапсуляцией?
В Smalltalk экземплярные переменные — приватны. Доступ только через методы. Методы — публичные, если не помечены иначе (в некоторых реализациях можно указать private). Нет ключевых слов private, protected, public — но дисциплина поддерживается соглашениями.
Person >> name
^ name
Person >> name: aName
name := aName
Код за пределами класса не должен напрямую читать или менять name — только через геттер и сеттер. Инкапсуляция достигается договорённостью и культурой, а не синтаксисом.
Полиморфизм в Smalltalk — естественное следствие посылки сообщений. Если разные объекты понимают одно и то же сообщение, они могут использоваться взаимозаменяемо.
Object subclass: #Shape
...
Shape subclass: #Circle
...
Shape subclass: #Rectangle
...
Оба класса реализуют:
Circle >> area
^ 3.14 * radius * radius
Rectangle >> area
^ width * height
Теперь можно писать:
| shapes total |
shapes := { Circle new. Rectangle new }.
total := 0.
shapes do: [ :each | total := total + each area ].
Никаких интерфейсов, аннотаций, типов.
Полиморфизм — duck typing: «если крякает как утка, плавает как утка — значит, это утка».
Интерфейсов и абстрактных классов в ST нет, ибо нет понятий interface или abstract class, как в Java или C#. Потому что они не нужны. Вместо интерфейсов — соглашения о сообщениях. Если объект отвечает на draw, move, area — он "реализует" этот «интерфейс». Это динамический полиморфизм, более гибкий, чем статические интерфейсы.
Вот такое вот ООП. Вроде бы изучали столько всего, а на практике в Smalltalk не так уж и много деталей, не правда ли?
Но на самом деле это лишь основы, ведь язык был одним из первых. Технологии идут вперёд, языки появляются и развиваются. Теперь давайте наконец начнём изучать более современные языки.